import * as d3 from "d3";
import * as _ from "lodash";

type SelectionType = d3.Selection<SVGElement, any, any, any>;

interface Config {
  scale?: number; // 设置minimap的尺寸，值为minimap相对于画布内容的比例
  maxSize?: number; // 限制minimap的最大宽高
  position?: "top-left" | "top-right" | "bottom-left" | "bottom-right"; // 小地图的位置
  left?: number; // 小地图距离左边的距离
  right?: number;
  top?: number;
  bottom?: number;
  initTransform?: d3.ZoomTransform; // 配置svg的初始transform
  scaleExtent?: number[]; // 限制画布的缩放
  padding?: number; // 限制画布的位移区域，最多可以超出内容的尺寸为padding
  backgroundFill?: string; // minimap背景
  backgroundStroke?: string; // minimap边框
  windowStroke?: string// 小地图窗口边框
}

const defaultConfig: Config = {
  scale: 0.2,
  maxSize: 200,
  position: "bottom-left",
  left: 16,
  right: 16,
  top: 16,
  bottom: 16,
  initTransform: new d3.ZoomTransform(1, 0, 0),
  scaleExtent: [0.1, 5],
  padding: 500,
  backgroundFill: "#fff",
  backgroundStroke: "rgba(219, 223, 230, 1)",
  windowStroke: "#005AFF",
};

export default class Minimap {
  svg: SelectionType;
  zoom: d3.ZoomBehavior<Element, any>;
  content: SelectionType;
  config: Config;
  minimap: SelectionType;
  backgroundBorder: SelectionType;
  miniwindow: SelectionType;

  constructor(svg: SelectionType, zoom?: d3.ZoomBehavior<Element, any>) {
    this.svg = svg;
    this.zoom = zoom;
    this.content = this.svg.selectChild("g") as SelectionType;
  }

  // 在调用了draw()之后调用
  getZoom() {
    return this.zoom;
  }

  /**
   * 在原svg中绘制minimap
   * @param customConfig 自定义配置
   */
  draw(customConfig?: Config) {
    this.config = _.merge(_.cloneDeep(defaultConfig), customConfig);
    this.destroy();

    const svgWidth = this.svg.node().getBoundingClientRect().width;
    const svgHeight = this.svg.node().getBoundingClientRect().height;
    console.log(svgWidth, svgHeight, 'svg size');
    const contentNode = this.content.node() as SVGGElement;
    const contentWidth = contentNode.getBBox().width + contentNode.getBBox().x * 2;
    const contentHeight = contentNode.getBBox().height + contentNode.getBBox().y * 2;
    console.log(contentWidth, contentHeight, 'content size');
    // 长宽不能超过config.maxSize
    this.config.scale = Math.min(this.config.scale, this.config.maxSize / Math.max(contentWidth, contentHeight));
    const minimapWidth = contentWidth * this.config.scale;
    const minimapHeight = contentHeight * this.config.scale;
    console.log(minimapWidth, minimapHeight, 'size');

    const displaySize = Math.max(minimapWidth, minimapHeight);
    // 计算小地图的坐标
    const [alignY, alignX] = this.config.position.split("-");
    let minimapPX;
    let minimapPY;
    if (alignY === "top") {
      minimapPY = this.config.top + (displaySize - minimapHeight) / 2;
    } else if (alignY === "bottom") {
      minimapPY = svgHeight - minimapHeight - this.config.bottom - (displaySize - minimapHeight) / 2;
    }
    if (alignX === "left") {
      minimapPX = this.config.left + (displaySize - minimapWidth) / 2;
    } else if (alignX === "right") {
      minimapPX = svgWidth - minimapWidth - this.config.right - (displaySize - minimapWidth) / 2;
    }

    // 在原svg中创建minimap
    this.minimap = this.svg
      .append("g")
      .attr("transform", `translate(${minimapPX}, ${minimapPY})`);

    // 添加背景
    this.minimap
      .append("rect")
      .attr("x", -(displaySize - minimapWidth) / 2)
      .attr("y", -(displaySize - minimapHeight) / 2)
      .attr("width", displaySize)
      .attr("height", displaySize)
      .attr("fill", this.config.backgroundFill);

    // const backgroundImg = document.createElement('img');
    // const svgData = new XMLSerializer().serializeToString(this.svg.node());
    // const canvas = document.createElement('canvas');
    // const ctx = canvas.getContext('2d');
    // const img = new Image();
    // const svgBlob = new Blob([svgData], { type: 'image/svg+xml;charset=utf-8' });
    // const url = URL.createObjectURL(svgBlob);
    // // 设置 Canvas 尺寸
    // canvas.width = contentWidth;
    // canvas.height = contentHeight;
    // img.onload = function () {
    //   // 绘制 SVG 到 Canvas
    //   ctx.drawImage(img, 0, 0);
    //   URL.revokeObjectURL(url);
    //   // 将 Canvas 转换为图像
    //   const imgURI = canvas.toDataURL('image/png').replace('image/png', 'image/octet-stream');
    //   backgroundImg.width = minimapWidth;
    //   backgroundImg.height = minimapHeight;
    //   backgroundImg.src = imgURI;
    // };
    // img.src = url;
    // // 创建 foreignObject 元素
    // const foreignObject = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject');
    // foreignObject.setAttribute('width', minimapWidth + '');
    // foreignObject.setAttribute('height', minimapHeight + '');
    // foreignObject.appendChild(backgroundImg);
    // // 将 foreignObject 元素添加到 SVG 中
    // this.minimap.append(() => foreignObject);

    // 添加内容缩略图
    const contentCopy = contentNode.cloneNode(true) as SVGGElement;
    contentCopy.setAttribute("transform", `scale(${this.config.scale})`);
    this.minimap.append(() => contentCopy);

    // 添加拖选框
    this.miniwindow = this.minimap
      .append("rect")
      .attr("width", svgWidth * this.config.scale)
      .attr("height", svgHeight * this.config.scale)
      .attr("fill", this.config.windowStroke)
      .attr("fill-opacity", 0.1)
      .attr("stroke", this.config.windowStroke);

    this.update(this.config.initTransform);

    // 添加边框
    this.backgroundBorder = this.minimap.append("rect")
      .attr("x", -(displaySize - minimapWidth) / 2)
      .attr("y", -(displaySize - minimapHeight) / 2)
      .attr("width", displaySize)
      .attr("height", displaySize)
      .attr("fill", "none")
      .attr("stroke", this.config.backgroundStroke)
      .attr("stroke-width", 2);

    // 创建默认的zoom behaviour
    this.zoom = d3
      .zoom()
      .scaleExtent(this.config.scaleExtent)
      .translateExtent([
        [-this.config.padding, -this.config.padding],
        [contentWidth + this.config.padding, contentHeight + this.config.padding],
      ])
      .on("zoom", (e) => {
        const content = this.svg.selectChild("g") as SelectionType;
        content.attr(
          "transform",
          `translate(${e.transform.x}, ${e.transform.y}) scale(${e.transform.k})`
        );
        this.update(e.transform);
      });

    // zoom行为挂载
    this.svg.call(this.zoom).on("dblclick.zoom", null);

    // 绑定drag事件
    this.miniwindow.call(
      d3.drag().on("drag", (e) => {
        this.zoom.translateBy(
          this.svg,
          -e.dx / this.config.scale,
          -e.dy / this.config.scale
        );
      })
    );
  }

  /**
   * 更新拖选框的位置，将svg内容的transform映射到拖选框上
   * @transform svg内容的transform
   */
  update(contentTransform: d3.ZoomTransform) {
    const svgWidth = this.svg.node().getBoundingClientRect().width;
    const svgHeight = this.svg.node().getBoundingClientRect().height;
    const rect = this.minimap.selectChild("rect").node().getBBox();

    const filterK = 1 / contentTransform.k;
    let x1 = -contentTransform.x * this.config.scale * filterK;
    let y1 = -contentTransform.y * this.config.scale * filterK;
    let x2 = x1 + svgWidth * this.config.scale * filterK;
    let y2 = y1 + svgHeight * this.config.scale * filterK;

    x1 = Math.max(x1, rect.x);
    y1 = Math.max(y1, rect.y);
    x2 = Math.min(x2, rect.x + rect.width);
    y2 = Math.min(y2, rect.y + rect.height);

    this.miniwindow.attr("x", x1);
    this.miniwindow.attr("y", y1);
    this.miniwindow.attr("width", Math.max(x2 - x1, 0));
    this.miniwindow.attr("height", Math.max(y2 - y1, 0));
  }

  destroy() {
    this.minimap?.remove();
  }
}
